#ifndef FUNCTION_H
#define FUNCTION_H
#include "Expression.h"
#include "Pattern.h"
#include "Lambda.h"
#include "TemplatePattern.cpp"
#define TOSTRING(x) (#x)

namespace TreeExpression {

//template <int numParam, typename Param1, typename Param2, typename Return, typename FunctionImpl> class Function : public Expression_CRTP< Function<numParam, Param1, Param2, Return, FunctionImpl> >, public Functor { };

/*template <typename Param1, typename Param2, typename Return, typename FunctionImpl> class Function<0, FunctionImpl, Return> : public Expression_CRTP< Function<0, FunctionImpl, Return> >, public Functor {
public:
	Function() : Expression() { }
	Function(const Function& copy) : Expression(copy) { }
	virtual ~Function() { }

	virtual AutoPointer<Expression> eval(CopyPointer<Context>& context) {
		return AutoPointer<Expression>(FunctionImpl::operator()());
	}

	//virtual std::string getHash() const;
	//virtual std::string toString() const;

private:

};

template <typename Param1, typename Param2, typename Return, typename FunctionImpl> class Function<1, FunctionImpl, Return> : public Expression_CRTP< Function<1, FunctionImpl, Return> >, public Functor {
public:
	Function() : Expression() { }
	Function(const Function& copy) : Expression(copy) {
		std::cout << "Copia corretta " << std::endl;
	}
	virtual ~Function() { }

	virtual AutoPointer<Expression> eval(CopyPointer<Context>& context) {
		typename FunctionImpl::Param1* param1 = dynamic_cast<typename FunctionImpl::Param1*>(this->getSon().get());//get the first parameter
		return AutoPointer<Expression>(FunctionImpl::operator()(param1));
	}

	//virtual std::string getHash() const;
	//virtual std::string toString() const;

private:

};*/

template <typename Param1, typename Return, typename FunctionImpl> class Function1 : public Expression_CRTP< Function1<Param1, Return, FunctionImpl> >, public Functor {
public:
	Function1(const std::string& name, const Pattern<>& pattern1) : Expression_CRTP< Function1<Param1, Return, FunctionImpl> >(), Functor(name) {
		this->addPattern(pattern1);
	}
	Function1(const Function1& copy) : Expression(copy), FlowExpression(copy), Expression_CRTP< Function1<Param1, Return, FunctionImpl> >(copy), Functor(copy) { }
	virtual ~Function1() { }

	virtual AutoPointer<Expression> eval(CopyPointer<Context>&) {
		FunctionImpl funz;
		Param1* param1 = dynamic_cast<Param1*>(this->getSon().get());//get the first parameter
		return AutoPointer<Expression>(funz(param1));
	}

	//virtual std::string getHash() const { return FunctionImpl::getHash(); }
	//TODO hash & co
};

template <typename Param1, typename Param2, typename Return, typename FunctionImpl> class Function2 : public Expression_CRTP< Function2<Param1, Param2, Return, FunctionImpl> >, public Functor {
public:
	Function2(const std::string& name, const Pattern<>& pattern1, const Pattern<>& pattern2) : Expression_CRTP< Function2<Param1, Param2, Return, FunctionImpl> >(), Functor(name) {
		this->addPattern(pattern2);//Reverse insert on the stack
		this->addPattern(pattern1);
	}
	Function2(const Function2& copy) : Expression(copy), FlowExpression(copy), Expression_CRTP< Function2<Param1, Param2, Return, FunctionImpl> >(copy), Functor(copy) { }
	virtual ~Function2() { }

	virtual AutoPointer<Expression> eval(CopyPointer<Context>&) {
		FunctionImpl funz;
		Param1* param1 = dynamic_cast<Param1*>(this->getSon().get());//get the first parameter
		Param2* param2 = dynamic_cast<Param2*>(this->getSon()->getLeaf().get());//get the second parameter
		return AutoPointer<Expression>(funz(param1, param2));
	}

	//virtual std::string getHash() const { return FunctionImpl::getHash(); }
	//TODO hash & co
};

}

template<typename Param1, typename Param2, typename Return, typename FunctionImpl>
	AutoPointer< TreeExpression::Expression >
		createFunction(const std::string& nameFunction, const Pattern<>& param1, const Pattern<>& param2) {
			return AutoPointer< TreeExpression::Expression >(new TreeExpression::Function2<Param1, Param2, Return, FunctionImpl>(nameFunction, param1, param2));
}

template<typename Param1, typename Return, typename FunctionImpl>
	AutoPointer< TreeExpression::Expression >
		createFunction(const std::string& nameFunction, const Pattern<>& param1) {
			return AutoPointer< TreeExpression::Expression >(new TreeExpression::Function1<Param1, Return, FunctionImpl>(nameFunction, param1));
}

#endif
